package controller.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.crypto.KeySelector.Purpose;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import sim_moteur.Simulateur;
import sim_moteur.Terrain;
import sim_moteur.element.animeaux.Animal;
import util.SimProperties;
import vue.Affichable;
import controleur.Controleur;
import controleur.SimulationControllerThread;
import controller.util.WatchDir;

public class ControllerXML {
	private static final String ABSOLUTE_PATH = "./output/";
	private static final String SIMULATION_ENTREE_AUTO = "simulation.entree.XML.automatique";

	private Controleur controleur;
	private WatchDir<String> watchDir;

	private File outputFolder = null;
	private String scenarioName = null;
	private int nombreSimulateur;

	private Boolean entreeAuto;

	public ControllerXML(Controleur controleur) {

		this.controleur = controleur;

	}

	/**
	 * Initialise le controller de fichiers XML
	 * 
	 * @param nom
	 *            le nom du nouveau scénario
	 * @param nombreSim
	 *            le nombre de simulateurs impliqués
	 */
	public void init(String nom, int nombreSim) {

		entreeAuto = SimProperties.getInstance().getBooleanProperty(
				SIMULATION_ENTREE_AUTO);

		this.scenarioName = nom;
		this.nombreSimulateur = nombreSim;
		outputFolder = new File(ABSOLUTE_PATH + scenarioName);

		if (watchDir == null && entreeAuto) {

			try {
				watchDir = new WatchDir<String>(new File("./input/").toPath());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			watchDir.processEvents((f) -> {
				this.readXMLAutomatique(f.toString());
				return new String("");
			});
		}

		deleteFolder(outputFolder);

		outputFolder.mkdirs();
	}

	public void writeXML(int noStep, SimulationControllerThread simController)
			throws XMLStreamException, FileNotFoundException {

		// Fichier de sortis XML
		FileOutputStream output = new FileOutputStream(ABSOLUTE_PATH
				+ scenarioName + "/step" + noStep + ".xml");

		// Fabrique pour XML
		XMLOutputFactory fabrique = XMLOutputFactory.newInstance();

		// Flux d'écriture XML
		XMLStreamWriter writer = fabrique.createXMLStreamWriter(output);

		// Début du document
		writer.writeStartDocument();

		// Ouverture de simualteur
		writer.writeStartElement("simulateur");

		writer.writeAttribute("xmlns:xsi",
				"http://www.w3.org/2001/XMLSchema-instance");
		writer.writeAttribute("xsi:schemaLocation",
				"http://www.example.org/cegep cegep.xsd ");
		writer.writeAttribute("numeroPasTemps", Integer.toString(noStep));
		writer.writeAttribute("nomScenario", scenarioName);
		writer.writeAttribute("dimensionTerrainX",
				Integer.toString(Terrain.getInstance().getXSize()));
		writer.writeAttribute("dimensionTerrainY",
				Integer.toString(Terrain.getInstance().getYSize()));
		writer.writeStartElement("element");

		// Ouverture de vivant
		writer.writeStartElement("vivant");

		for (Affichable animaux : simController.getAnimaux()) {
			writer.writeStartElement("animal");
			writer.writeAttribute("coordX",
					Integer.toString(animaux.getCoordonnees().getX()));
			writer.writeAttribute("coordY",
					Integer.toString(animaux.getCoordonnees().getY()));
			writer.writeAttribute("vie", Float.toString(animaux.getVie()));
			writer.writeCharacters(animaux.getName());
			writer.writeEndElement();
		}

		for (Affichable vegetaux : simController.getVegetaux()) {
			writer.writeStartElement("vegetal");
			writer.writeAttribute("coordX",
					Integer.toString(vegetaux.getCoordonnees().getX()));
			writer.writeAttribute("coordY",
					Integer.toString(vegetaux.getCoordonnees().getY()));
			writer.writeAttribute("vie", Float.toString(vegetaux.getVie()));
			writer.writeCharacters(vegetaux.getName());
			writer.writeEndElement();
		}

		// Fermeture de vivant
		writer.writeEndElement();

		for (Affichable choses : simController.getLesChosesBasiques()) {
			writer.writeStartElement("choses");
			writer.writeAttribute("coordX",
					Integer.toString(choses.getCoordonnees().getX()));
			writer.writeAttribute("coordY",
					Integer.toString(choses.getCoordonnees().getY()));
			writer.writeEndElement();
		}

		// Fermeture d'élément
		writer.writeEndElement();

		// Fermeture de simulateur
		writer.writeEndElement();
		writer.writeEndDocument();
		writer.flush();
		writer.close();
		try {
			output.flush();
			output.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void readXMLAutomatique(String fichierXML) {
		// Vous devez lire le fichier de commande xml ici!

		InputStream input = null;

		try {
			input = new FileInputStream("./input/" + fichierXML);
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		XMLInputFactory fabrique = XMLInputFactory.newInstance();
		XMLStreamReader reader = null;

		try {
			reader = fabrique.createXMLStreamReader(input);
		} catch (XMLStreamException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		fabrique.setXMLReporter((message, typeErreur, source, location) -> {
			System.out.println("Erreur de type: " + typeErreur + ", message : "
					+ message);
		});
		Commande commande = null;
		String dernierTexte = null;
		try {
			while (reader.hasNext()) {
				int event = reader.next();
				switch (event) {
				case XMLStreamConstants.START_ELEMENT:
					switch (reader.getLocalName()) {
					case "commande":
						commande = new Commande();

						scenarioName = reader.getAttributeValue(1);
						int periodeTemp = Integer.parseInt(reader
								.getAttributeValue(0));

						break;
					case "vivant":
						for (int i = 0; i < nombreSimulateur; i++) {
							commande.setCommande(scenarioName + "0" + "."
									+ "simulation.animal.nombre",
									reader.getAttributeValue(0));

							commande.setCommande(scenarioName + "0" + "."
									+ "simulation.vegetaux.nombre",
									reader.getAttributeValue(1));
						}
						break;
					case "chose":
						for (int i = 0; i < nombreSimulateur; i++) {
							commande.setCommande(scenarioName + "0" + "."
									+ "simulation.chose.nombre",
									reader.getAttributeValue(0));
						}
						break;
					}
					break;
				case XMLStreamConstants.END_ELEMENT:
					switch (reader.getLocalName()) {
					case "commande":
						SimProperties.getInstance().setCommande(
								commande.convertToProps());
						break;
					case "x":
						// TODO gérer les dimensions non carré
						commande.setCommande("simulation.terrain.x.dimension",
								dernierTexte);
						break;
					case "y":
						commande.setCommande("simulation.terrain.y.dimension",
								dernierTexte);
						break;
					}
					break;
				case XMLStreamConstants.CHARACTERS:
					dernierTexte = reader.getText().trim();
					break;
				}
			}
		} catch (XMLStreamException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			input.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		controleur.init(nombreSimulateur, scenarioName);
	}

	/**
	 * détruit tous les dossiers de façon récurrente
	 * 
	 * @param folder
	 *            le dossier à détruire
	 */
	public static void deleteFolder(File folder) {
		File[] fichiers = folder.listFiles();
		if (fichiers != null) { // some JVMs return null for empty dirs
			for (File fichier : fichiers) {
				if (fichier.isDirectory()) {
					deleteFolder(fichier);
				} else {
					fichier.delete();
				}
			}
		}
		folder.delete();
	}

}
